﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using STPlatform.UPracticalMSSQLDatabaseV2;

namespace SimpleORM
{
    public abstract class CDAttribute : Attribute, ICD
    {
        #region Methods

        #region Methods - Virtual

        public virtual object Clone()
        {
            object newObj = MemberwiseClone();

            newObj.TraversalPropertiesInfo(ClonePropertyHandler, newObj);

            return newObj;
        }

        public virtual void Dispose()
        {
            //this.TraversalPropertiesInfo(DisposePropertyHandler);
        }

        #endregion

        #region Methods - Private

        private bool ClonePropertyHandler(PropertyInfo pi, object value, object target)
        {
            if (!pi.CanWrite)
                return true;

            pi.SetValue(target, CDObject.TryClone(value), null);

            return true;
        }

        private bool DisposePropertyHandler(string name, object value)
        {
            IDisposable id = value as IDisposable;
            if (id == null)
                return true;

            id.Dispose();

            return true;
        }

        #endregion

        #endregion
    }

    public static class ICustomAttributeProviderExtension
    {
        #region Methods
        public static T GetAttribute<T>(this ICustomAttributeProvider source) where T : Attribute
        {
            object[] objs = source.GetCustomAttributes(typeof(T), true);
            if (objs.Length == 0)
                return null;

            return (T)objs[0];
        }
        public static T[] GetAttributes<T>(this ICustomAttributeProvider source) where T : Attribute
        {
            object[] objs = source.GetCustomAttributes(typeof(T), true);

            List<T> listAttr = new List<T>();
            foreach (object obj in objs)
                listAttr.Add((T)obj);

            return listAttr.ToArray<T>();
        }

        public static Attribute GetAttribute(this ICustomAttributeProvider source, Type attrType)
        {
            object[] objs = source.GetCustomAttributes(attrType, true);
            if (objs.Length == 0)
                return null;

            return (Attribute)objs[0];
        }

        #endregion
    }
    [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
    public class DotNetTypeAttribute : Attribute
    {
        #region Properties

        public Type Type { get; set; }

        #endregion

        #region Fields

        private static Dictionary<eDataType, Type> dicDataTypeToType = new Dictionary<eDataType, Type>();
        private static Dictionary<Type, eDataType> dicTypeToDataType = new Dictionary<Type, eDataType>();

        #endregion

        #region Ctor

        static DotNetTypeAttribute()
        {
            Install();
        }

        public DotNetTypeAttribute(Type type)
        {
            Type = type;
        }

        #endregion

        #region Methods

        private static void Install()
        {
            eDataType.Object.TraversalFieldsInfo((fi, val) =>
            {
                if (!fi.FieldType.IsEnum)
                    return true;
                
                var attr = fi.GetAttribute<DotNetTypeAttribute>();
                if (attr == null)
                    attr = new DotNetTypeAttribute(typeof(object));

                dicDataTypeToType[(eDataType)val] = attr.Type;
                if (!dicTypeToDataType.ContainsKey(attr.Type))
                    dicTypeToDataType[attr.Type] = (eDataType)val;

                return true;
            });
        }

        public static Type GetType(eDataType type)
        {
            return dicDataTypeToType[type];
        }

        public static eDataType GetType(Type type)
        {
            while (type != null)
            {
                if (dicTypeToDataType.ContainsKey(type))
                    return dicTypeToDataType[type];

                type = type.BaseType;
            }

            throw new ArgumentNullException("type");
        }

        #endregion
    }

    public interface IDbSchema : IDataSourceSchema
    {
        #region Properties

        IDbContext DataSource { get; set; }

        #endregion

        #region Methods

        int Save();

        #endregion
    }
    public interface IDataSourceSchema : ICD
    {
        #region Methods

        void Export(Type elementType);
        Type Import(string schema, string elementName);
        DynamicClassBuilder ImportBuilder(string schema, string tableName);

        void CreateElement(Type elementType);
        void DeleteElement(string schema, string elementName);
        void RenameElement(string schema, string elementName, string elementNameNew);
        bool AnyElement(string schema, string elementName);

        void CreateElementProperty(string schema, string elementName, DataColumnAttribute property);
        void DeleteElementProperty(string schema, string elementName, string propertyName);
        void ChangeElementProperty(string schema, string elementName, DataColumnAttribute property);
        bool AnyElementProperty(string schema, string elementName, string propertyName);

        List<string> GetElementPropertyNames(string schema, string elementName);
        List<string> GetElementPrimaryPropertyNames(string schema, string elementName);
        List<DataColumnAttribute> GetElementProperties(string schema, string elementName);
        List<DataTableAttribute> GetElements();

        List<ElementRelationship> GetElementRelationships();
        List<ElementRelationship> GetElementRelationshipsDirection();

        #endregion
    }
}
